package com.remoting.core.http;

import com.remoting.api.RemoteSetting;
import com.remoting.api.registry.bean.HostInfo;
import com.remoting.api.request.RequestType;
import com.remoting.api.request.support.RemoteRequest;
import com.remoting.core.BaseRemoteServiceExecutor;
import com.remoting.core.exception.RemoteException;
import com.remoting.core.exception.TimeoutException;
import com.remoting.core.support.hystrix.HystrixUtil;

import java.util.function.Function;


/**
 * 基于HTTP通信协议的远程服务调用器
 * <p>
 * 说明:
 * </p>
 * <li></li>
 * 
 * @author DuanYong
 * @since 2017年3月3日上午10:18:13
 */
public class HttpRemoteServiceExecutor extends BaseRemoteServiceExecutor {
	/** 请求超时*/
	public static final String REQUEST_TIMEOUT= "REQUEST_TIMEOUT";
	/**
	 * 默认构造器
	 */
	public HttpRemoteServiceExecutor() {
		super(new RemoteSetting());
	}
	/**
	 * 构造器
	 */
	public HttpRemoteServiceExecutor(RemoteSetting config) {
		super(config);
	}

	@Override
	protected String doRequest(RemoteRequest remoteRequest) {
		//默认超时时间：建立连接时间+从连接池中获取连接的timeout时间+数据传输处理时间
		int timeout = this.getRemoteSetting().getConnectionTimeout()+this.getRemoteSetting().getConnectionRequestTimeout()+this.getRemoteSetting().getSocketTimeout();
		//如果设置了超时时间
		if(remoteRequest.getServiceImportInfo().getTimeout()>0){
			timeout = remoteRequest.getServiceImportInfo().getTimeout();
		}
		Object r = HystrixUtil.execute(remoteRequest.getServiceImportInfo().getServiceId(),timeout, executeFunction,fallbackFunction, remoteRequest);
		if(REQUEST_TIMEOUT.equals(r)){
			throw new TimeoutException();
		}
		return String.valueOf(r);
	}

	/**
	 * 服务调用
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @auther DuanYong
	 * @since 2017年3月3日上午10:51:30
	 * @param remoteRequest 请求对象
	 * @return 返回报文
	 */
	private String invoke(RemoteRequest remoteRequest) {
		int i = 0;
		for (HostInfo host : remoteRequest.getServiceImportInfo().getEndpoint().getHosts()) {
			try {
				if (RequestType.get.value().equals(remoteRequest.getServiceImportInfo().getRequestType())) {
					return getHttpHandler().get(remoteRequest.builderGetRequest(host));
				} else {
					return getHttpHandler().post(remoteRequest.builderPostRequest(host));
				}
			} catch (Exception e) {
				e.printStackTrace();
				if ((i + 1) == remoteRequest.getServiceImportInfo().getEndpoint().getHosts().size()) {
					throw new RemoteException("HTTP服务请求失败，尝试所有的主机地址后均失败.服务ID='" + remoteRequest.getServiceImportInfo().getServiceId() + "'");
				}
				logger.warn("请求主机'" + host.toUrl() + "'失败, 准备向其它主机请求尝试. 服务ID='" + remoteRequest.getServiceImportInfo().getServiceId() + "'");
			}
			i++;
		}
		throw new RemoteException("未配置目标主机地址.服务ID='" + remoteRequest.getServiceImportInfo().getServiceId() + "'");
	}

	/**
	 * 执行业务处理
	 */
	private Function<RemoteRequest, Object> executeFunction = (RemoteRequest p) -> {
		return invoke(p);
	};

	/**
   	 * 执行降级业务处理
   	 */
   	private Function<RemoteRequest,Object> fallbackFunction = (RemoteRequest p)->{
   		return fallback(p);
    };
    /**
	 * 服务降级，默认返回REQUEST_TIMEOUT字符串，框架统一处理抛出TimeoutException异常
	 * <p>说明:</p>
	 * <li>注意：在fallback方法中不允许有远程方法调用，方法尽量要轻，调用其他外部接口也要进行hystrix降级。否则执行fallback方法会抛出异常</li>
	 * @author DuanYong
	 * @param p 参数
	 * @return REQUEST_TIMEOUT
	 * @since 2018年8月21日上午11:20:37
	 */
    private Object fallback(RemoteRequest p){
		return REQUEST_TIMEOUT;
	}
}
